/**
GPL v3.0
Copyright (c) 2017 by Hui Lu
*/

#include <WiFi.h>
#include <HTTPClient.h>
#include <SPI.h>

#include "EPD_drive.h"
#include "EPD_drive_gpio.h"
#include "bitmaps.h"

#include "FS.h"
#include "SPIFFS.h"

const char *ssid = "HalfSweet"; //我的WiFissid与密码（懒得写配网了，就直接写死得了
const char *password = "HalfSweet";

const char *ntpServer = "ntp.aliyun.com";                                               //ntp服务器
const long gmtOffset_sec = 8 * 3600;                                                    //时钟偏移
const int daylightOffset_sec = 0;                                                       //夏令时
unsigned AllMin, NEMTMin, SubMin, LocalYear, LocalMonth, LocalDay, LocalHour, LocalMin; //获取到的时间

HTTPClient HTTP;
Duck_EPD EPD; //墨水屏驱动

void DisConnected(void); //刷新背景图片
void GetLocalTime(void); //获取当前时间
void SetLocalTime(void);
void Countdown(void); //倒计时
int TrueLeap(void);   //判断是否为闰年
void WakeDis(void);   //深度睡眠醒来后刷新分钟
void TimeDis(void);   //正常情况下刷新

#define uS_TO_S_FACTOR 1000000ULL                                            /* 从微秒转换到秒 */
#define TIME_TO_SLEEP 60                                                     /* ESP32进入睡眠时间（以秒为单位） */
RTC_DATA_ATTR unsigned long LastYear, LastMonth, LastDay, LastHour, LastMin; //剩下的年月日时分，存在RTC RAM里面，唤醒后可读取

String StrLastYear, StrLastMonth, StrLastDay, StrLastHour, StrLastMin;

void setup()
{
  pinMode(CS, OUTPUT); //io初始化
  pinMode(DC, OUTPUT);
  pinMode(RST, OUTPUT);
  pinMode(BUSY, INPUT);
  pinMode(CLK, OUTPUT);
  pinMode(DIN, OUTPUT);
  SPIFFS.begin();
  EPD.EPD_Set_Model(WX29); //选择墨水屏型号
  Serial.begin(115200);
  //delay(2000);
  esp_sleep_wakeup_cause_t wakeup_reason;
  wakeup_reason = esp_sleep_get_wakeup_cause();
  Serial.println(wakeup_reason);

  if (wakeup_reason == ESP_SLEEP_WAKEUP_TIMER) //如果是从深度睡眠中唤醒的，那么就刷新一下分钟
  {
    WakeDis();
  }
  else
  {
    DisConnected(); //显示背景
    Serial.println("ok");
    GetLocalTime(); //获取当前时间
    Countdown();
    Serial.println("ok");
    TimeDis(); //显示数字
    Serial.println("ok");
    esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
    esp_deep_sleep_start();
  }
}

void loop()
{
  yield();
}

void DisConnected()
{
  EPD.EPD_init_Full(); //全刷初始化，使用全刷波形
  EPD.clearbuffer();   //清空缓存(全白)
  EPD.fontscale = 1;
  EPD.DrawXbm_P(0, 0, 296, 128, (unsigned char *)Connected); //画连接中图像
  EPD.EPD_Dis_Full((unsigned char *)EPD.EPDbuffer, 1);       //将缓存中的图像传给屏幕控制芯片刷新屏幕
  EPD.ReadBusy_long();                                       //等待屏幕刷新完成后才继续往下运行
  EPD.deepsleep();
  Serial.println("ok");
}

void GetLocalTime()
{
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected!");

  // 从网络时间服务器上获取并设置时间
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  SetLocalTime();
  Serial.println(LocalYear);
  Serial.println(LocalMonth);
  Serial.println(LocalDay);
  Serial.println(LocalHour);
  Serial.println(LocalMin);

  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
  Serial.println("WiFi disconnected!");
}

void SetLocalTime()
{
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo))
  {
    Serial.println("Failed to obtain time");
    return;
  }
  //Serial.println(&timeinfo, "%F %T %A"); // 格式化输出当前时间
  LocalYear = timeinfo.tm_year + 1900;
  LocalMonth = timeinfo.tm_mon;
  LocalDay = timeinfo.tm_mday;
  LocalHour = timeinfo.tm_hour;
  LocalMin = timeinfo.tm_min;
}

void Countdown()
{
  if (LocalMonth < 6)
  {
    LastYear = LocalYear;
    switch (LocalMonth - 1)
    {
    case 0:
      AllMin = 0;
      break;
    case 1:
      AllMin = 31;
      break;
    case 2:
      AllMin = 31 + TrueLeap();
      break;
    case 3:
      AllMin = 31 + TrueLeap() + 31;
      break;
    case 4:
      AllMin = 31 + TrueLeap() + 31 + 30;
      break;
    case 5:
      AllMin = 31 + TrueLeap() + 31 + 30 + 31;
      break;
    }
    AllMin = (((AllMin + LocalDay - 1) * 24 + LocalHour - 1) * 60) + LocalMin;
    NEMTMin = (((31 + TrueLeap() + 31 + 30 + 31 + 6) * 24 + 8) * 60);
    SubMin = NEMTMin - AllMin;
    LastDay = SubMin / (60 * 24);
    LastHour = (SubMin - LastDay * 60 * 24) / 60;
    LastMin = (SubMin - LastHour * 60 - LastDay * 60 * 24);
    if (LastMin > 0)
      LastMin = LastMin - 1;
  }
  else if (LocalMonth == 6)
  {
    if (LocalDay <= 7)
    {
      AllMin = (((LocalDay - 1) * 24 + LocalHour - 1) * 60) + LocalMin;
      NEMTMin = (6 * 24 + 8) * 60;
      SubMin = NEMTMin - AllMin;
      LastDay = SubMin / (60 * 24);
      LastHour = (SubMin - LastDay * 60 * 24) / 60;
      LastMin = (SubMin - LastHour * 60 - LastDay * 60 * 24);
      if (LastMin > 0)
        LastMin = LastMin - 1;
    }
    else
    {
      LastYear = LocalYear + 1;
      AllMin = (((LocalDay - 1) * 24 + LocalHour - 1) * 60) + LocalMin;
      NEMTMin = (6 * 24 + 8) * 60;
      if (LastYear % 4 == 0 && LastYear % 400 == 0)
        SubMin = 366 * 24 * 60 - AllMin + NEMTMin;
      SubMin = 365 * 24 * 60 - AllMin + NEMTMin;
      LastDay = SubMin / (60 * 24);
      LastHour = (SubMin - LastDay * 60 * 24) / 60;
      LastMin = (SubMin - LastHour * 60 - LastDay * 60 * 24);
      if (LastMin > 0)
        LastMin = LastMin - 1;
    }
  }
  else
  {
    LastYear = LocalYear + 1;
    switch (LocalMonth - 1)
    {
    case 6:
      AllMin = 31 + TrueLeap() + 31 + 30 + 31 + 30;
      break;
    case 7:
      AllMin = 31 + TrueLeap() + 31 + 30 + 31 + 30 + 31;
      break;
    case 8:
      AllMin = 31 + TrueLeap() + 31 + 30 + 31 + 30 + 31 + 31;
      break;
    case 9:
      AllMin = 31 + TrueLeap() + 31 + 30 + 31 + 30 + 31 + 31 + 30;
      break;
    case 10:
      AllMin = 31 + TrueLeap() + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31;
      break;
    case 11:
      AllMin = 31 + TrueLeap() + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30;
      break;
    }
    AllMin = (((AllMin + LocalDay - 1) * 24 + LocalHour - 1) * 60) + LocalMin;
    NEMTMin = (((31 + TrueLeap() + 31 + 30 + 31 + 6) * 24 + 8) * 60);
    if (LastYear % 4 == 0 && LastYear % 400 == 0)
      SubMin = 366 * 24 * 60 - AllMin + NEMTMin;
    SubMin = 365 * 24 * 60 - AllMin + NEMTMin;
    LastDay = SubMin / (60 * 24);
    LastHour = (SubMin - LastDay * 60 * 24) / 60;
    LastMin = (SubMin - LastHour * 60 - LastDay * 60 * 24);
    if (LastMin > 0)
      LastMin = LastMin - 1;
  }
}

int TrueLeap()
{
  if (LocalYear % 4 == 0 && LocalYear % 400 == 0)
    return 29;
  else
    return 28;
}

void WakeDis()
{
  //LastMin = 8;
  LastMin++; //分钟数+1
  StrLastMin = String(LastMin);
  if (LastMin == 60) //如果到达下一小时就全刷一次
    ESP.restart();
  EPD.EPD_init_Part(); //局刷初始化
  EPD.clearbuffer();
  EPD.fontscale = 2;
  EPD.SetFont(FONT12);
  if (LastMin < 10) //判断分钟数为两位数还是一位数，一位数前面加空格以对齐
    EPD.DrawUTF(94, 206 + 12, StrLastMin);
  else
    EPD.DrawUTF(94, 204, StrLastMin);
  EPD.EPD_Dis_Part(88, 127, 206, 206 + 12 + 12, (unsigned char *)EPD.EPDbuffer, 1);
  EPD.ReadBusy();
  EPD.deepsleep();                                               //屏幕休眠
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); //进入deepsleep
  esp_deep_sleep_start();
}

void TimeDis()
{
  StrLastYear = String(LastYear); //把要显示的int型数字全部转化为String
  StrLastMonth = String(LastMonth);
  StrLastDay = String(LastDay - 30);
  StrLastHour = String(LastHour);
  StrLastMin = String(LastMin);
  Serial.println(StrLastYear);
  Serial.println(StrLastDay);
  Serial.println(LastHour);
  Serial.println(StrLastMin);

  EPD.EPD_init_Full(); //局刷初始化
  EPD.clearbuffer();   //清空缓存(全白)
  EPD.fontscale = 1;
  EPD.DrawXbm_P(0, 0, 296, 128, (unsigned char *)Background);
  EPD.fontscale = 2;
  EPD.SetFont(FONT12); //选择字体

  EPD.DrawUTF(10, 70, StrLastYear); //显示年份
  Serial.println("OK");

  if (LastDay > 99)                  //判断天数为几位数
    EPD.DrawUTF(94, 28, StrLastDay); //显示天数
  else if (LastDay > 9)
    EPD.DrawUTF(94, 28 + 12, StrLastDay);
  else
    EPD.DrawUTF(94, 28 + 12 + 12, StrLastDay);
  Serial.println("OK");

  if (LastHour > 9)                    //判断小时数为几位数
    EPD.DrawUTF(94, 109, StrLastHour); //显示小时数
  else
    EPD.DrawUTF(94, 109 + 12, StrLastHour);
  if (LastMin > 9)                    //判断分钟数为几位数
    EPD.DrawUTF(94, 206, StrLastMin); //显示分钟数
  else
    EPD.DrawUTF(94, 206 + 12, StrLastMin);
  EPD.EPD_Dis_Full((unsigned char *)EPD.EPDbuffer, 1);
  EPD.ReadBusy_long();
  Serial.println("全刷");
  EPD.deepsleep();
}